//
//  TaskDelegate.swift
//  NetWork
//
//  Created by Delin Meng 孟德林 on 2018/7/30.
//  Copyright © 2018年 Delin Meng 孟德林. All rights reserved.
//

import UIKit

open class TaskDelegate: NSObject {

    /// 操作队列
    open let queue:OperationQueue
    
    /// 服务器回调的数据
    public var data:Data? { return nil }
    
    /// 错误信息
    public var error:Error?
    
    /// 任务
    var task:URLSessionTask? {
        
        set{
            taskLock.lock(); defer { taskLock.unlock() }
            _task = newValue
        }
        get {
            taskLock.lock(); defer { taskLock.unlock() }
            return _task
        }
    }
    /// 初始化时钟时间
    var initialResponseTime:CFAbsoluteTime?
    
    /// URL证书
    var credential:URLCredential?
    
    var metrics:AnyObject? // URLSessionTaskMetrics
    
    private var _task: URLSessionTask? {
        didSet { reset() }
    }
    private let taskLock = NSLock()
    
    
    //MARK: 生命周期
    
    init(task:URLSessionTask?) {
        _task = task
        self.queue = {
            let operationQueue = OperationQueue()
            operationQueue.isSuspended = true
            operationQueue.qualityOfService = .utility
            return operationQueue

            /*

             public enum QualityOfService : Int {


             case userInteractive  // 用户互动

             case userInitiated  // 用户发起

             case utility  // 程序运行中

             case background  // 后台

             case `default`
             }




             */


        }()
    }
    /// 重新配置
    func reset() {
        error = nil
        initialResponseTime = nil
    }
    
    
    var taskWillPerformHTTPRedirection:((URLSession,URLSessionTask,HTTPURLResponse,URLRequest) -> URLRequest?)?
    
    var taskDidReceiveChallenge:((URLSession,URLSessionTask,URLAuthenticationChallenge) -> (URLSession.AuthChallengeDisposition, URLCredential?))?
    
    var taskNeedNewBobyStream:((URLSession,URLSessionTask) -> InputStream?)?
    
    var taskDidCompleteWithError:((URLSession,URLSessionTask,Error?) -> Void)?
    
    
    func urlSession(_ session:URLSession,
                    task:URLSessionTask,
                    willPerformHTTPRedirection response:HTTPURLResponse,
                    newRequest request:URLRequest,
                    completionHandler:(URLRequest?) -> Void) { // URLSession.ResponseDisposition 响应处理
        var redirectRequest:URLRequest? = request
        if let taskWillPerformHTTPRedirection = taskWillPerformHTTPRedirection {
            redirectRequest = taskWillPerformHTTPRedirection(session, task, response, request)
        }
        completionHandler(redirectRequest)
    }
    
    
    func urlSession(_ session:URLSession,
                    task:URLSessionTask,
                    didReceive challenge: URLAuthenticationChallenge, // 身份验证挑战
        completionHandler:(URLSession.AuthChallengeDisposition,URLCredential?) -> Void){


        // disposition 部署处置

        var disposition:URLSession.AuthChallengeDisposition = .performDefaultHandling
        var credential:URLCredential?
        
        if let taskDidReceiveChallenge = taskDidReceiveChallenge {
            
            (disposition,credential) = taskDidReceiveChallenge(session,task,challenge)
            
        }else if challenge.protectionSpace.authenticationMethod == NSURLAuthenticationMethodServerTrust{
            /// 保护空间 身份验证方法 == 身份验证服务器信任
            let host = challenge.protectionSpace.host
            if let serverTrustPolicy = session.serverTrustPolicyManager?.serverTrustPolicy(forHost: host),
                let serverTrust = challenge.protectionSpace.serverTrust {

                if serverTrustPolicy.evaluate(serverTrust, forHost: host) {
                    disposition = .useCredential
                    credential = URLCredential(trust: serverTrust)
                }else{
                    disposition = .cancelAuthenticationChallenge
                }
            }
        }else{

            if challenge.previousFailureCount > 0 {
                disposition = .rejectProtectionSpace
            }else{

                credential = self.credential ?? session.configuration.urlCredentialStorage?.defaultCredential(for: challenge.protectionSpace)
                if credential != nil {
                    disposition = .useCredential
                }
            }
        }
        completionHandler(disposition,credential)

    }
    
    
    public func urlSession(
        _ session: URLSession,
        task: URLSessionTask,
        needNewBodyStream completionHandler: @escaping (InputStream?) -> Void)
    {
        var bodyStream: InputStream?
        
        if let taskNeedNewBodyStream = taskNeedNewBobyStream {
            bodyStream = taskNeedNewBodyStream(session, task)
        }
        
        completionHandler(bodyStream)
    }


    @objc(URLSession:task:didCompleteWithError:)
    public func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
        if let taskDidCompleteWithError = taskDidCompleteWithError {
            taskDidCompleteWithError(session, task, error)
        } else {
            if let error = error {
                if self.error == nil { self.error = error }

                if
                    let downloadDelegate = self as? DownloadTaskDelegate,
                    let resumeData = (error as NSError).userInfo[NSURLSessionDownloadTaskResumeData] as? Data
                {
                    downloadDelegate.resumeData = resumeData
                }
            }

            queue.isSuspended = false
        }
    }




}

open class DataTaskDelegate: TaskDelegate,URLSessionDataDelegate {
    

    var dataTask:URLSessionDataTask { return task as! URLSessionDataTask }

    public override var data: Data? {
        if dataStream != nil {
            return nil

        }else{
            return mutableData
        }
    }

    var progress:Progress
    var progressHandler:(closure:Request.ProgressHandler,queue:DispatchQueue)?
    var dataStream:((_ data:Data) -> Void)?

    private var totalBytesReceived: Int64 = 0
    private var mutableData:Data
    private var expectedContentLength:Int64?


    override init(task: URLSessionTask?) {

        mutableData = Data()
        progress = Progress(totalUnitCount: 0)
        super.init(task: task)
    }


    override func reset() {

        super.reset()
        progress = Progress(totalUnitCount: 0)
        totalBytesReceived = 0
        mutableData = Data()
        expectedContentLength = nil
    }


    // MARK: URLSessionDataDelegate

    var dataTaskDidReceiveResponse: ((URLSession, URLSessionDataTask, URLResponse) -> URLSession.ResponseDisposition)?
    var dataTaskDidBecomeDownloadTask: ((URLSession, URLSessionDataTask, URLSessionDownloadTask) -> Void)?
    var dataTaskDidReceiveData: ((URLSession, URLSessionDataTask, Data) -> Void)?
    var dataTaskWillCacheResponse: ((URLSession, URLSessionDataTask, CachedURLResponse) -> CachedURLResponse?)?

    // ResponseDisposition 响应处理
    public func urlSession(
        _ session: URLSession,
        dataTask: URLSessionDataTask,
        didReceive response: URLResponse,
        completionHandler: @escaping (URLSession.ResponseDisposition) -> Void)
    {
        var disposition: URLSession.ResponseDisposition = .allow
        expectedContentLength = response.expectedContentLength
        if let dataTaskDidReceiveResponse = dataTaskDidReceiveResponse {
            disposition = dataTaskDidReceiveResponse(session, dataTask, response)
        }
        completionHandler(disposition)
    }


    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didBecome downloadTask: URLSessionDownloadTask) {
        dataTaskDidBecomeDownloadTask?(session,dataTask,downloadTask)
    }


    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {

        if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
        if let dataTaskDidReceiveData  = dataTaskDidReceiveData {
            dataTaskDidReceiveData(session,dataTask,data)
        }else{
            if let dataStream = dataStream {
                dataStream(data)
            }else{
                mutableData.append(data)
            }
            let bytesReceived = Int64(data.count)
            totalBytesReceived += bytesReceived
            let totalBytesExpected = dataTask.response?.expectedContentLength ?? NSURLSessionTransferSizeUnknown
            progress.totalUnitCount = totalBytesExpected
            progress.completedUnitCount = totalBytesReceived

            if let progressHandler = progressHandler {
                progressHandler.queue.async { progressHandler.closure(self.progress) }
            }
        }
    }


    public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, willCacheResponse proposedResponse: CachedURLResponse, completionHandler: @escaping (CachedURLResponse?) -> Void) {

        var cachedResponse:CachedURLResponse? = proposedResponse
        if let dataTaskWillCacheResponse = dataTaskWillCacheResponse {
            cachedResponse = dataTaskWillCacheResponse(session,dataTask,proposedResponse)
        }
        completionHandler(cachedResponse)
    }
}


class DownloadTaskDelegate: TaskDelegate,URLSessionDownloadDelegate {


    var downloadTask:URLSessionDownloadTask { return task as! URLSessionDownloadTask }
    var progress:Progress
    var progressHandler: (closure:Request.ProgressHandler,queue:DispatchQueue)?

    var resumeData:Data?
    override var data:Data? { return resumeData }

    // 目的地
    var destination:DownloadRequest.DownloadFileDestination?

    // 临时的网址
    var temporaryURL:URL?

    // 目的地网址
    var destinationURL:URL?

    // 文件 url
    var fileURL:URL? { return destination != nil ? destinationURL : temporaryURL }

    override init(task: URLSessionTask?) {
        progress = Progress(totalUnitCount: 0)
        super.init(task: task)
    }

    override func reset() {
        super.reset()
        progress = Progress(totalUnitCount: 0)
        resumeData = nil
    }

    // MARK: URLSessionDownloadDelegate

    var downloadTaskDidFinishDownloadingToURL: ((URLSession, URLSessionDownloadTask, URL) -> URL)?
    var downloadTaskDidWriteData: ((URLSession, URLSessionDownloadTask, Int64, Int64, Int64) -> Void)?
    var downloadTaskDidResumeAtOffset: ((URLSession, URLSessionDownloadTask, Int64, Int64) -> Void)?

    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
        temporaryURL = location
        guard let destination = destination, let response = downloadTask.response as? HTTPURLResponse else { return }
        let result = destination(location,response)
        let destinationURL = result.destinationURL
        let options = result.options
        self.destinationURL = destinationURL

        do {
            if options.contains(.removePreviousFile),FileManager.default.fileExists(atPath: destinationURL.path) {
                try FileManager.default.removeItem(at: destinationURL)
            }

            if options.contains(.createIntermediateDirectories) {
                let directory = destinationURL.deletingPathExtension()
                try FileManager.default.createDirectory(at: directory, withIntermediateDirectories: true)
            }
            try FileManager.default.moveItem(at: location, to: destinationURL)
        }catch {
            self.error = error
        }
    }


    func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didWriteData bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) {

        if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
        if let downloadTaskDidWriteData = downloadTaskDidWriteData {
            downloadTaskDidWriteData(session,downloadTask,bytesWritten,totalBytesWritten,totalBytesExpectedToWrite)
        }else{
            progress.totalUnitCount = totalBytesExpectedToWrite
            progress.completedUnitCount = totalBytesWritten

            if let progressHandler = progressHandler {
                progressHandler.queue.async {
                    progressHandler.closure(self.progress)
                }
            }
        }
    }

    func urlSession(
        _ session: URLSession,
        downloadTask: URLSessionDownloadTask,
        didResumeAtOffset fileOffset: Int64,
        expectedTotalBytes: Int64)
    {
        if let downloadTaskDidResumeAtOffset = downloadTaskDidResumeAtOffset {
            downloadTaskDidResumeAtOffset(session, downloadTask, fileOffset, expectedTotalBytes)
        } else {
            progress.totalUnitCount = expectedTotalBytes
            progress.completedUnitCount = fileOffset
        }
    }

}


class UploadTaskDelegate: DataTaskDelegate {

    var uploadTask:URLSessionUploadTask { return task as! URLSessionUploadTask }

    var uploadProgress:Progress

    var uploadProgressHandler:(closure: Request.ProgressHandler,queue: DispatchQueue)?

    override init(task: URLSessionTask?) {
        uploadProgress = Progress(totalUnitCount: 0)
        super.init(task: task)
    }

    override func reset() {
        super.reset()
        uploadProgress = Progress(totalUnitCount: 0)
    }

    var taskDidSendBobyData: ((URLSession,URLSessionTask,Int64,Int64,Int64) -> Void)?

    func urlSession(_ session: URLSession, task: URLSessionTask, didSendBodyData bytesSent: Int64, totalBytesSent: Int64, totalBytesExpectedToSend: Int64) {

        if initialResponseTime == nil { initialResponseTime = CFAbsoluteTimeGetCurrent() }
        if let taskDidSendBobyData = taskDidSendBobyData {
            taskDidSendBobyData(session,task,bytesSent,totalBytesSent,totalBytesExpectedToSend)
        }else {
            uploadProgress.totalUnitCount = totalBytesExpectedToSend
            uploadProgress.completedUnitCount = totalBytesSent
            if let uploadProgressHandler = uploadProgressHandler {
                uploadProgressHandler.queue.async { uploadProgressHandler.closure(self.uploadProgress) }
            }
        }
    }
}

